// -----------------------------------------------------------------------
//  <copyright file="Extensions.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/24 21:27</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Exceptions;

using Microsoft.Extensions.Configuration;


namespace DaprPlus.EntityFrameworkCore;

public static class Extensions
{
    /// <summary>
    /// 添加数据上下文
    /// </summary>
    public static IServiceCollection AddDbContext<TDbContext>(this IServiceCollection services, Action<DbContextOptionsBuilder, DatabaseOptions> action)
        where TDbContext : DbContextBase
    {
        //services.AddTransient<DispatchDomainEventInterceptor>();
        services.AddDbContext<TDbContext>((provider, builder) =>
        {
            //builder.AddInterceptors(provider.GetRequiredService<DispatchDomainEventInterceptor>());
            var options = provider.GetService<IOptions<DatabaseOptions>>()?.Value;
            if (options == null || string.IsNullOrEmpty(options.ConnectionString))
            {
                throw new DaprPlusException(ResEFCore.String_Set_DatabaseOptions);
            }
            action.Invoke(builder, options);
        });
        services.AddScoped<IDbContext>(provider => provider.GetRequiredService<TDbContext>());
        return services;
    }

    /// <summary>
    /// 获取主库连接字符串
    /// </summary>
    public static string? GetMasterConnectionString(this IServiceProvider provider)
    {
        var opts = provider.GetService<IOptions<DatabaseOptions>>()?.Value;
        return opts?.ConnectionString;
    }

    /// <summary>
    /// 获取主库连接字符串
    /// </summary>
    public static string? GetMasterConnectionString(this IConfiguration configuration)
    {
        var opts = new DatabaseOptions();
        configuration.Bind(DatabaseOptions.Position, opts);
        return opts.ConnectionString;
    }

    /// <summary>
    /// 添加EFCore模块功能
    /// </summary>
    public static IServiceCollection AddEntityFrameworkCore(this IServiceCollection services)
    {
        services.Configure<DatabaseOptions>(DatabaseOptions.Position);
        services.AddScoped(typeof(IQueryRepository<>), typeof(QueryRepository<>));
        services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
        services.AddScoped<IUnitOfWork, UnitOfWork>();
        services.AddScoped<ISpecificationEvaluator, SpecificationEvaluator>();
        services.AddSingleton<ISlaveDatabaseSelector, WeightSlaveDatabaseSelector>();
        services.AddSingleton<IEntityBatchConfiguration, SoftDeleteQueryFilterConfiguration>();
        services.AddSingleton<IEntityBatchConfiguration, PropertyUtcDateTimeConfiguration>();

        return services;
    }

    /// <summary>
    /// 检测并迁移数据库
    /// </summary>
    public static void CheckAndMigration<TDbContext>(this IServiceProvider provider)
        where TDbContext : DbContext
    {
        provider.ExecuteScopedWork(scopedProvider =>
        {
            var context = scopedProvider.GetRequiredService<TDbContext>();
            var logger = provider.GetLogger("ServiceProviderExtensions.CheckAndMigration");
            context.CheckAndMigration(logger);
        });
    }

    /// <summary>
    /// 检测并初始化种子数据
    /// </summary>
    public static async Task CheckAndSeedAsync(this IServiceProvider provider)
    {
        var seeders = provider.GetServices<IEntitySeeder>();
        foreach (var seeder in seeders)
        {
            await seeder.SeedAsync();
        }
    }

    /// <summary>
    /// 获取所有实体注册器
    /// </summary>
    /// <param name="provider"></param>
    /// <returns></returns>
    public static IEntityRegister[] GetEntityRegisters(this IServiceProvider provider)
    {
        var context = provider.GetRequiredService<DaprPlusContext>();
        var types = context.FindTypesByBase<IEntityRegister>();
        var objs = types.Select(type => (Activator.CreateInstance(type) as IEntityRegister)!).ToArray();
        return objs;
    }
}
